package com.example.mybatisplusproject.util.lock;

import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.concurrent.TimeUnit;

/**
 * redis分布式锁
 * 仅当前线程可以解锁的分布式锁
 *
 * @author zls
 * @date 2022/6/3 9:42 下午
 */
@Component
@Slf4j
public class RedisDistributedLock {

    public static final String delScript =
            "local v = redis.call('get',KEYS[1]);\n" +
            "if v then \n" +
            "\tif v~=ARGV[1] then \n" +
            "\t\treturn 0;\n" +
            "\tend;\n" +
            "\tredis.call('del',KEYS[1]);\n" +
            "end;\n" +
            "return 1;\n";

    @Resource
    RedisTemplate<String, String> redisTemplate;

    public boolean lock(String key) {
        return Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(key, generateValue()));
    }

    public boolean lock(String key, long lockTime) {
        return Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(key, generateValue(), lockTime, TimeUnit.MILLISECONDS));
    }

    public boolean lock(String key, long lockTime, long waitTime) {
        long startTime = System.currentTimeMillis();

        while (System.currentTimeMillis() - startTime <= waitTime) {
            boolean succeed = lock(key, lockTime);
            if (succeed) {
                return true;
            }
            try {
                Thread.sleep(10);
            } catch (InterruptedException e) {
                log.error("thread interrupted, ", e);
            }
        }
        return false;
    }

    /**
     * 仅当前线程可以解锁
     * @param key
     * @return
     */
    public boolean unLock(String key) {
        DefaultRedisScript<Long> script = new DefaultRedisScript<Long>(delScript, Long.class);
        Long res = redisTemplate.execute(script, Collections.singletonList(key), generateValue());
        return res == 1;
    }


    private String generateValue() {
        return String.valueOf(Thread.currentThread().getId());
    }
}
